import { assert } from 'cc';
import { ActionSegment } from './ActionSegment';
import { ActionSpec } from './ActionSpec';
import { ActuatorDiscreteActionMask } from './ActuatorDiscreteActionMask';
import { ActionBuffers } from './IActionReceiver';
import { IActuator } from './IActuator';

/**
 * 专门管理用于管理一系列IActuators事件委托，行为缓冲器，和动作掩码
 */
export class ActuatorManager{
	/**
	 * 要管理的执行器
	 */
	private _actuators: Array<IActuator> = null!;
	public get actuators(): Array<IActuator> {
		return this._actuators;
	}
	public set actuators(value: Array<IActuator>) {
		this._actuators = value;
	}
	

	/**
	 * 离散行为掩码实现 允许基于偏移量写入
	 */
	private _discreteActionMask: ActuatorDiscreteActionMask = null!;

	/**
	 * 联合行为
	 */
	private _combinedActionSpec: ActionSpec = null!;

	private _readyForExecution: boolean = false;

	private _sumOfDiscreteBranchSizes: number = 0;
	public get sumOfDiscreteBranchSizes(): number {
		return this._sumOfDiscreteBranchSizes;
	}
	public set sumOfDiscreteBranchSizes(value: number) {
		this._sumOfDiscreteBranchSizes = value;
	}

	private _numDiscreteActions: number = 0;
	public get numDiscreteActions(): number {
		return this._numDiscreteActions;
	}
	public set numDiscreteActions(value: number) {
		this._numDiscreteActions = value;
	}

	private _numContinuousActions: number = 0;
	public get numContinuousActions(): number {
		return this._numContinuousActions;
	}
	public set numContinuousActions(value: number) {
		this._numContinuousActions = value;
	}

	public get totalNumberOfActions() {
		return this.numContinuousActions + this.numDiscreteActions;
	}

	get discreteActionMask() {
		return this._discreteActionMask;
	}

	private _storedActions: ActionBuffers = null!;
	public get storedActions(): ActionBuffers {
		return this._storedActions;
	}
	public set storedActions(value: ActionBuffers) {
		this._storedActions = value;
	}

	/**
	 * 创建预制容量的执行器列表
	 * @param capacity 容积
	 */
	constructor(capacity: number) {
		this._actuators = new Array<IActuator>();
	}

	private readyActuatorsForExecution(): void;
	private readyActuatorsForExecution(
		actuators: Array<IActuator>,
		numContinuousActions: number,
		sumOfDiscreteBranches: number,
		numDiscreteBranches: number
	): void;

	/**
	 *  此方法验证所有驱动器的名称是否唯一（unique）
	 * @param actuators 要验证（validate）和分配缓冲区的执行器列表
	 * @param numContinuousActions 所有执行器的连续动作总数
	 * @param sumOfDiscreteBranches 所有执行器的离散分支的总和，以便能够分配一个IDiscreteActionMask
	 * @param numDiscreteBranches  所有执行器的离散分支数量
	 * @returns
	 */
	private readyActuatorsForExecution(
		actuators?: Array<IActuator>,
		numContinuousActions?: number,
		sumOfDiscreteBranches?: number,
		numDiscreteBranches?: number
	): void {
		if (this._readyForExecution) {
			return;
		}

		actuators = actuators ?? this._actuators;
		numContinuousActions = numContinuousActions ?? this.numContinuousActions;
		sumOfDiscreteBranches = sumOfDiscreteBranches ?? this.sumOfDiscreteBranchSizes;
		numDiscreteBranches = numDiscreteBranches ?? this.numDiscreteActions;

		//Sort the Actuators by name to ensure (确保) determinism（确定性）
		ActuatorManager.sortActuators(this._actuators);
		const continuousActions =
			this.numContinuousActions == 0
				? ActionSegment.empty
				: new ActionSegment(new Array<number>(numContinuousActions));
		const discreteActions =
			this.numDiscreteActions == 0
				? ActionSegment.empty
				: new ActionSegment(new Array<number>(numDiscreteBranches));

		this.storedActions = new ActionBuffers(continuousActions, discreteActions);
		this._combinedActionSpec = ActuatorManager.combineActionSpecs(actuators);
		this._discreteActionMask = new ActuatorDiscreteActionMask(
			actuators,
			sumOfDiscreteBranches,
			numDiscreteBranches,
			this._combinedActionSpec.branchSizes
		);
		this._readyForExecution = true;
	}

	static sortActuators(actuators: IActuator[]) {
		actuators.sort((x, y) => x.name.localeCompare(y.name, undefined, { sensitivity: 'base' }));
	}

	static combineActionSpecs(actuators: IActuator[]): ActionSpec {
		let numContinuousActions: number = 0;
		let numDiscreteActions: number = 0;

		actuators.forEach((actuator) => {
			numContinuousActions += actuator.actionSpec.numContinuousActions;
			numDiscreteActions += actuator.actionSpec.numDiscreteActions;
		});

		let combinedBranchSizes: number[];
		if (numDiscreteActions === 0) {
			combinedBranchSizes = [];
		} else {
			combinedBranchSizes = new Array<number>(numDiscreteActions);
			let start = 0;
			for (let i = 0; i < actuators.length; i++) {
				const branchSizes = actuators[i].actionSpec.branchSizes;
				if (branchSizes) {
					branchSizes.forEach((size) => {
						combinedBranchSizes[start] = size;
						start++;
					});
				}
			}
		}

		return new ActionSpec(numContinuousActions, combinedBranchSizes);
	}

	getCombinedActionSpec() {
		this.readyActuatorsForExecution();
		return this._combinedActionSpec;
	}

	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	updateActions(actions: ActionBuffers) {
		this.readyActuatorsForExecution();
		ActuatorManager.updateActionArray(actions.continuousActions, this.storedActions.continuousActions);
		ActuatorManager.updateActionArray(actions.discreteActions, this.storedActions.discreteActions);
	}
	

	static updateActionArray(sourceActionBuffer: ActionSegment, destination: ActionSegment) {
		if (sourceActionBuffer.length <= 0) {
			destination.clear();
		} else {
			if (sourceActionBuffer.length !== destination.length) {
				assert(
					sourceActionBuffer.length === destination.length,
					`sourceActionBuffer: ${sourceActionBuffer.length}
                    is a different size than destination: ${destination.length}`
				);
			}

			let start = sourceActionBuffer.offset;
			for (let index = destination.offset; index < destination.length; index++) {
				destination.setValue(index,sourceActionBuffer.getValue(start));
				start++;
			}
		}
	}

	writeActionMask() {
		this.readyActuatorsForExecution();
		this._discreteActionMask.resetMask();
		let offset = 0;
		for (let i = 0; i < this._actuators.length; i++) {
			const actuator = this._actuators[i];
			if (actuator.actionSpec.numDiscreteActions > 0) {
				this._discreteActionMask.currentBranchOffset = offset;
				actuator.writeDiscreteActionMask(this._discreteActionMask);
				offset += actuator.actionSpec.numDiscreteActions;
			}
		}
	}

	/**
	 * 遍历这个列表中的所有IActuators并调用它们的heuristic方法。
	 * 如果实现了方法，则根据它们的动作规范选择合适的动作段。
	 * @param actionBuffersOut
	 */
	applyHeuristic(actionBuffersOut: ActionBuffers) {
		let continuousStart = 0;
		let discreteStart = 0;

		for (let i = 0; i < this._actuators.length; i++) {
			const actuator = this._actuators[i];
			const numContinuousActions = actuator.actionSpec.numContinuousActions;
			const numDiscreteActions = actuator.actionSpec.numDiscreteActions;

			if (numContinuousActions == 0 && numDiscreteActions == 0) {
				continue;
			}

			let continuousActions = ActionSegment.empty;
			if (numContinuousActions > 0) {
				continuousActions = new ActionSegment(
					actionBuffersOut.continuousActions.data,
					continuousStart,
					numContinuousActions
				);
			}

			let discreteActions = ActionSegment.empty;
			if (numDiscreteActions > 0) {
				discreteActions = new ActionSegment(
					actionBuffersOut.discreteActions.data,
					discreteStart,
					numDiscreteActions
				);
			}

			actuator.heuristic(new ActionBuffers(continuousActions, discreteActions));
			continuousStart += numContinuousActions;
			discreteStart += numDiscreteActions;
		}
	}

	/**
	 * 遍历列表中的所有执行器并且调用他们的IActionReceiver.OnActionReceived方法。
	 * 根据他们的行为规范选项适合的行为片段。
	 */
	executeActions() {
		this.readyActuatorsForExecution();
		let continuousStart = 0;
		let discreteStart = 0;
		for (let i = 0; i < this._actuators.length; i++) {
			const actuator = this._actuators[i];
			const numContinuousActions = actuator.actionSpec.numContinuousActions;
			const numDiscreteActions = actuator.actionSpec.numDiscreteActions;

			if (numContinuousActions == 0 && numDiscreteActions == 0) {
				continue;
			}

			let continuousActions = ActionSegment.empty;
			if (numContinuousActions > 0) {
				continuousActions = new ActionSegment(
					this.storedActions.continuousActions.data,
					continuousStart,
					numContinuousActions
				);
			}

			let discreteActions = ActionSegment.empty;
			if (numDiscreteActions > 0) {
				discreteActions = new ActionSegment(
					this.storedActions.discreteActions.data,
					discreteStart,
					numDiscreteActions
				);
			}

			actuator.onActionReceived(new ActionBuffers(continuousActions, discreteActions));
			continuousStart += numContinuousActions;
			discreteStart += numDiscreteActions;
		}
	}

	/**
	 * 重置所有ActionBuffers为0并且调用每一个IActuator的resetDat方法，由该对象管理。
	 */
	resetData() {
		if (!this._readyForExecution) {
			return;
		}
		this.storedActions.clear();
		for (let i = 0; i < this._actuators.length; i++) {
			this._actuators[i].resetData();
		}
		this.discreteActionMask.resetMask();
	}

	/**
	 * 验证当前管理的IActuators永远唯一的名称
	 * 每个Actuator都需要有一个唯一的名称，以确保action buffer的存储和Actuator的执行在不同的会话中都是确定的。
	 */
	validateActuators() {
		for (let i = 0; i < this._actuators.length - 1; i++) {
			assert(this._actuators[i].name !== this._actuators[i + 1].name, `Actuator names must be unique`);
		}
	}

	/**
	 * 辅助方法，当执行器添加到此对象的执行时，更新缓冲区管理周围的簿记项。
	 * @param actuatorItem 需要记账的执行器
	 * @returns
	 */
	addToBufferSizes(actuatorItem: IActuator) {
		if (!actuatorItem) {
			return;
		}

		this.numContinuousActions += actuatorItem.actionSpec.numContinuousActions;
		this.numDiscreteActions += actuatorItem.actionSpec.numDiscreteActions;
		this.sumOfDiscreteBranchSizes += actuatorItem.actionSpec.sumOfDiscreteBranchSize;
	}

	/**
	 * 辅助方法，当执行器从这个对象移除时，更新缓冲区管理周围的簿记项。
	 * @param actuatorItem
	 */
	subtractFromBufferSize(actuatorItem: IActuator) {
		if (!actuatorItem) {
			return;
		}

		this.numContinuousActions -= actuatorItem.actionSpec.numContinuousActions;
		this.numDiscreteActions -= actuatorItem.actionSpec.numDiscreteActions;
		this.sumOfDiscreteBranchSizes -= actuatorItem.actionSpec.sumOfDiscreteBranchSize;
	}

	/**
	 * 设置簿记项为0
	 */
	clearBufferSizes() {
		this.numContinuousActions = 0;
		this.numDiscreteActions = 0;
		this.sumOfDiscreteBranchSizes = 0;
	}

	addActuators(actuators: IActuator[]) {
		for (let i = 0; i < actuators.length; i++) {
			const actuator = actuators[i];
			this.add(actuator);
		}
	}

	add(item: IActuator) {
		assert(
			this._readyForExecution == false,
			'Cannot add to the ActuatorManager after its buffers have been initialized'
		);
		this._actuators.push(item);
		this.addToBufferSizes(item);
	}

	clear() {
		assert(
			this._readyForExecution == false,
			'Cannot clear the ActuatorManager after its buffers have been initialized'
		);
		this._actuators = [];
	}

	contains(item: IActuator) {
		return this._actuators.includes(item);
	}

	copyTo(array: IActuator[], arrayIndex: number) {
		for (let i = arrayIndex; i < this._actuators.length; i++) {
			const item = this._actuators[i];
			array.push(item);
		}
	}

	remove(item: IActuator) {
		assert(
			this._readyForExecution == false,
			'Cannot remove the ActuatorManager after its buffers have been initialized'
		);

		this._actuators.splice(this._actuators.indexOf(item), 1);
		this.subtractFromBufferSize(item);
	}

	get count(): number {
		return this._actuators.length;
	}

	get isReadOnly() {
		return false;
	}

	indexOf(item: IActuator) {
		return this._actuators.indexOf(item);
	}

	insert(index: number, item: IActuator) {
		assert(
			this._readyForExecution == false,
			'Cannot insert the ActuatorManager after its buffers have been initialized'
		);

		this._actuators.splice(index, 0, item);
	}

	removeAt(index: number) {
		assert(
			this._readyForExecution == false,
			'Cannot removeAt the ActuatorManager after its buffers have been initialized'
		);
		this._actuators.splice(index, 1);
	}

	getItemByIndex(index: number): IActuator {
		return this._actuators[index];
	}

	setItemByIndex(index: number, value: IActuator) {
		assert(
			this._readyForExecution == false,
			'Cannot modify the ActuatorManager after its buffers have been initialized'
		);

		const old = this._actuators[index];
		this.subtractFromBufferSize(old);
		this._actuators[index] = value;
		this.addToBufferSizes(value);
	}

    [index:number]:number;
}
